package org.mcflier.appuntamento.tasks;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.mcflier.appuntamento.core.Factory;
import org.mcflier.appuntamento.core.exception.ModulesException;
import org.mcflier.appuntamento.core.logger.LoggerManager;
import org.mcflier.appuntamento.core.modules.IModule;
import org.mcflier.appuntamento.core.modules.ModuleNode;

public class TaskManager implements IModule {

	private static final String TASKS_TAG = "tasks";
	private static final String TASK_TAG = "task";
	private static final String NAME_TAG = "name";
	private static final String DELAY_TAG = "delay";
	private static final String PATH_TAG = "path";
	private static final String ARGS_TAG = "args";
	private static final String ARG_TAG = "arg";
	private static final String ARG_NAME_TAG ="name";
	private static final String ARG_VALUE_TAG = "value";

	private Map<String, AbstractTask> tasks;
	private Logger logger;

	public TaskManager() throws ModulesException{
		this.tasks = new HashMap<String, AbstractTask>();
		this.logger = ((LoggerManager)Factory.getInstance().getModuleInstance(Factory.MODULE_LOGGER)).getLogger(this.getClass().getName());
	}

	@Override
	public void receiveModuleContent(ModuleNode node) {
		node = node.getNode(TASKS_TAG);
		if(node.getNodes().size()==0 && node.getAttributes().size()!=0){
			Set<String> keys = node.getAttributes().keySet();
			ModuleNode temp = new ModuleNode(TASK_TAG);
			for (String key : keys) {
				temp.add(key, node.getAttribute(key));
			}
			node.getAttributes().clear();
			node.add(temp.getName(), temp);
		}

		Set<String> keys = node.getNodes().keySet();
		AbstractTask task;
		String name, path;
		long delay = 6000;
		ModuleNode temp;

		for (String key : keys) {
			temp = node.getNode(key);
			name = temp.getAttribute(NAME_TAG);
			if(name==null){
				this.logger.warning("Error retrieving a without name. Name is completely necessary");
				break;
			}

			try{
				delay = Long.parseLong(temp.getAttribute(DELAY_TAG));
				if(delay<60000){
					delay = 60000;
					this.logger.warning("Task delay '"+name+"' increased to 60000 (minimum delay)");
				}
			}
			catch(NumberFormatException e){
				this.logger.warning("Error retieving the task '"+name+"'. It has an invalid delay (it must be an integer number greater than zero, expressed in millis)");
				break;
			}

			path = temp.getAttribute(PATH_TAG);
			if(path == null){
				this.logger.warning("Error retrievin the task '"+name+"'. It has not a task path");
				break;
			}

			try {
				task = (AbstractTask)Class.forName(path).getConstructors()[0].newInstance(name, delay);

				temp = temp.getNode(ARGS_TAG);
				if(temp != null){
					if(temp.getAttributes().size()>0){
						ModuleNode arg = new ModuleNode(ARG_TAG);
						Set<String> argkeys = temp.getAttributes().keySet();
						for (String argKey : argkeys) {
							arg.add(argKey, temp.getAttribute(argKey));
						}

						temp.add(arg.getName(), arg);
					}

					Set<String> argkeys = temp.getNodes().keySet();
					for (String argkey : argkeys) {
						ModuleNode argument = temp.getNode(argkey);
						task.addArgument(argument.getAttribute(ARG_NAME_TAG), argument.getAttribute(ARG_VALUE_TAG));
					}
				}

				this.tasks.put(task.getName(), task);
			} catch (IllegalArgumentException e) {
				this.logger.warning("Error creating the task '"+name+"'. The constructor receives different parameters");
				break;
			} catch (SecurityException e) {
				this.logger.warning("Error creating the task '"+name+"'.\n"+e.getMessage());
				break;
			} catch (InstantiationException e) {
				this.logger.warning("Error creating the task '"+name+"'. This class must be instantiable (not abstract, interface ...)");
				break;
			} catch (IllegalAccessException e) {
				this.logger.warning("Error creating the task '"+name+"'. This class needs a public constructor and it must be an instantiable class");
				break;
			} catch (InvocationTargetException e) {
				this.logger.warning("Error creating the task '"+name+"'.\n"+e.getMessage());
				break;
			} catch (ClassNotFoundException e) {
				this.logger.warning("Error creating the task '"+name+"'. The class '"+path+"' is missing");
				break;
			}
		}
	}

	public AbstractTask getTask(String name){
		return this.tasks.get(name);
	}

	public void beginTasks(){
		Collection<AbstractTask> tasks = this.tasks.values();
		for (AbstractTask task : tasks) {
			this.beginTask(task);
		}
	}

	public void beginTask(AbstractTask task){
		this.logger.info("Launching task: "+task.getName());
		task.start();
	}

	public void stopTasks(){
		Collection<AbstractTask> tasks = this.tasks.values();
		for (AbstractTask task : tasks) {
			this.stopTask(task);
		}
	}

	public void stopTask(AbstractTask task){
		this.logger.info("Stopping task: "+task.getName());
		task.stopTask();
	}
}